<?php
	$raw=array(
		"base"=>array(
			"vitality"=>10,
			"magic"=>10,
			"constitution"=>10,
			"endurance"=>10,
			"control"=>10,
			"intelligence"=>10,
			"hunger"=>100,
			"tiredness"=>100
		),
		"current"=>array(
			"vitality"=>10,
			"magic"=>10,
			"constitution"=>10,
			"endurance"=>10,
			"control"=>2,
			"intelligence"=>40,
			"hunger"=>0,
			"tiredness"=>0
		)
	);
	
	$test=<<<RUNE
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
	environment:12
	fire
	78
	person:this
	harm;
RUNE;

	class response
	{
		function __construct($init=array())
		{
			$this->data=$init;
		}
		
		public function send()
		{
			die(json_encode($this->data));
		}
	}
	
	class spell
	{
		public static $rune=array(
			"environment"=>"environment",
			"vessel"=>"vessel",
			"person"=>"person",
			"all"=>"all",
			"this"=>"this",
			"fire"=>"fire",
			"earth"=>"earth",
			"water"=>"water",
			"air"=>"air",
			"energy"=>"energy",
			"matter"=>"matter",
			"soul"=>"soul",
			"body"=>"body",
			"mind"=>"mind",
			"harm"=>"harm",
			"help"=>"help"
		);
		
		public static $seperators=array(";"," ",":");
		
		function __construct($arg)
		{
			if(is_numeric($arg))
			{
				$info=$this->pull($arg);
				$this->id=$info["id"];
				$this->readWords($info["words"]);
				$this->energy=$info["energy"];
				$this->matter=$info["matter"];
			}
			else
			{
				$this->readWords($arg);
				$this->energy=0;
				$this->matter=0;
			}
			
			$this->part_index=0;
		}
		
		public function cast($part_num=1)
		{
			for($max_index=$this->part_index+$part_num;$this->part_index<$max_index;$this->part_index++)
			{
				if(!$this->commit($this->part_index))
				{
					$this->end();
				}
				unset($this->word_list[$this->part_index]);
				unset($this->parts[$this->part_index]);
			}
		}
		
		public function commit($index=null)
		{
			global $response;
			if($index===null)
			{
				$index=$this->part_index;
			}
			if(!isset($this->parts[$index]))
			{
				return 0;
			}
			if(!isset($response->data["commited"]))
			{
				$response->data["commited"]=array();
			}
			$response->data["commited"][]=$this->parts[$index];
		}
		
		public function turn()
		{
			global $response;
			$this->cast(2);
			$this->push();
			$response->data["casted"]=$this->id;
		}
		
		private function end()
		{
			
		}
		
		public function pull($id)
		{
			global $db;
			$res=$db->query("SELECT * FROM `spell` WHERE `id`='{$id}'");
			return $res->fetch_assoc();
		}
		
		public function push()
		{
			global $db;
			$words=implode("",$this->word_list);
			if(!isset($this->id))
			{
				$db->query("INSERT INTO `spell` VALUE (NULL,'{$words}',{$this->energy},{$this->matter})");
				$this->id=$db->insert_id;
			}
			else
			{
				$db->query("UPDATE `spell` SET `words`='{$words}', `energy`='{$this->energy}', `matter`='{$this->matter}' WHERE `id`={$this->id}");
			}
		}
		
		public function readWords($words)
		{
			if(!self::validateWords($words))
			{
				trigger_error("words not valid!\n'".$words."'");
				return false;
			}
			$trune=array_flip(static::$rune);
			$this->parts=array();
			$this->word_list=array();
			foreach(explode(static::$seperators[0],$words) as $key=>$part)
			{
				if($part=="")
				{
					break;
				}
				$part=trim($part);
				$this->word_list[]=$part.static::$seperators[0];
				$this->parts[$key]=array();
				list($source,$element,$amount,$target,$action)=explode(static::$seperators[1],$part);
				
				list($type,$which)=explode(static::$seperators[2],$source);
				$which=(isset($trune[$which]))?$trune[$which]:$which;
				$type=$trune[$type];
				
				$this->parts[$key]["source"]=array("type"=>$type,"which"=>$which);
				
				$this->parts[$key]["element"]=$trune[$element];
				$this->parts[$key]["amount"]=$amount;
				
				list($type,$which,$where)=explode(static::$seperators[2],$target.static::$seperators[2]);
				$which=(isset($trune[$which]))?$trune[$which]:explode(static::$seperators[3],$which);
				$type=$trune[$type];
				$where=(isset($trune[$where]))?$trune[$where]:null;
				$this->parts[$key]["target"]=array("type"=>$type,"which"=>$which,"where"=>$where);
				
				$this->parts[$key]["action"]=$trune[$action];
			}
		}
		
		private static function validateWords(&$words)
		{
			$words=self::cleanWords($words);
			$rune=&static::$rune;
			$seperators=&static::$seperators;
			
			$type="({$rune["environment"]})|({$rune["vessel"]})|({$rune["person"]})";
			$select="\d+";
			$which="({$select})|({$rune["all"]})|({$rune["this"]})";
			$source="({$type}){$seperators[2]}({$which})";
			$element="({$rune["fire"]})|({$rune["earth"]})|({$rune["water"]})|({$rune["air"]})|({$rune["energy"]})|({$rune["matter"]})";
			$amount="\d+";
			$where="({$rune["soul"]})|({$rune["body"]})|({$rune["mind"]})";
			$target="({$type}){$seperators[2]}({$which})({$seperators[2]}{$where}){0,1}";
			$action="({$rune["harm"]})|({$rune["help"]})";
			$magic="({$source}){$seperators[1]}({$element}){$seperators[1]}({$amount}){$seperators[1]}({$target}){$seperators[1]}({$action}){$seperators[0]}({$seperators[1]}){0,1}";
			
			return preg_match("/^({$magic})+$/",$words);
		}
		
		private static function cleanWords(&$words)
		{
			return preg_replace("/\s+/"," ",trim($words));
		}
	}
	
	class stats
	{
		function __construct($raw)
		{
			if(!$raw)
			{
				return;
			}
			
			$structure=array(
				"base"=>array(
					"soul"=>array("vitality"=>$raw["base"]["vitality"],"magic"=>$raw["base"]["magic"]),
					"body"=>array("constitution"=>$raw["base"]["constitution"],"endurance"=>$raw["base"]["endurance"]),
					"mind"=>array("control"=>$raw["base"]["control"],"intelligence"=>$raw["base"]["intelligence"])
				),
				"current"=>array(
					"soul"=>array("vitality"=>$raw["current"]["vitality"],"magic"=>$raw["current"]["magic"]),
					"body"=>array("constitution"=>$raw["current"]["constitution"],"endurance"=>$raw["current"]["endurance"]),
					"mind"=>array("control"=>$raw["current"]["control"],"intelligence"=>$raw["current"]["intelligence"])
				)
			);
			
			$needs=array(
				"base"=>array("hunger"=>$raw["base"]["hunger"],"tiredness"=>$raw["base"]["tiredness"]),
				"current"=>array("hunger"=>$raw["current"]["hunger"],"tiredness"=>$raw["current"]["tiredness"])
			);
			
			$structure["base"]["soul"][0]=&$structure["base"]["soul"]["vitality"];
			$structure["base"]["soul"][1]=&$structure["base"]["soul"]["magic"];
			$structure["base"]["body"][0]=&$structure["base"]["body"]["constitution"];
			$structure["base"]["body"][1]=&$structure["base"]["body"]["endurance"];
			$structure["base"]["mind"][0]=&$structure["base"]["mind"]["control"];
			$structure["base"]["mind"][1]=&$structure["base"]["mind"]["intelligence"];
			
			$structure["base"]["vitality"]=&$structure["base"]["soul"]["vitality"];
			$structure["base"]["magic"]=&$structure["base"]["soul"]["magic"];
			$structure["base"]["constitution"]=&$structure["base"]["body"]["constitution"];
			$structure["base"]["endurance"]=&$structure["base"]["body"]["endurance"];
			$structure["base"]["control"]=&$structure["base"]["mind"]["control"];
			$structure["base"]["intelligence"]=&$structure["base"]["mind"]["intelligence"];
			
			$structure["current"]["soul"][0]=&$structure["current"]["soul"]["vitality"];
			$structure["current"]["soul"][1]=&$structure["current"]["soul"]["magic"];
			$structure["current"]["body"][0]=&$structure["current"]["body"]["constitution"];
			$structure["current"]["body"][1]=&$structure["current"]["body"]["endurance"];
			$structure["current"]["mind"][0]=&$structure["current"]["mind"]["control"];
			$structure["current"]["mind"][1]=&$structure["current"]["mind"]["intelligence"];
			
			$structure["current"]["vitality"]=&$structure["current"]["soul"]["vitality"];
			$structure["current"]["magic"]=&$structure["current"]["soul"]["magic"];
			$structure["current"]["constitution"]=&$structure["current"]["body"]["constitution"];
			$structure["current"]["endurance"]=&$structure["current"]["body"]["endurance"];
			$structure["current"]["control"]=&$structure["current"]["mind"]["control"];
			$structure["current"]["intelligence"]=&$structure["current"]["mind"]["intelligence"];
			
			$well_being=array("metabolism"=>null,"morale"=>null);
			
			$this->base=array("structure"=>$structure["base"],"needs"=>$needs["base"],"well_being"=>$well_being);
			$this->current=array("structure"=>$structure["current"],"needs"=>$needs["current"],"well_being"=>$well_being);
			
			$this->reStat("base");
			$this->reStat("current");
			$this->setWellBeing();
			$this->fixAction();
			
		}
		
		private function reStat($which)
		{
			$stat_group=&$this->$which;
			$stat_group["ability"]=static::makeAbility($stat_group["structure"]);
			$stat_group["action"]=static::makeAction($stat_group["structure"]);
		}
		
		private static function makeAbility($struct)
		{
			$strength=2*$struct["vitality"]+4*$struct["constitution"]+2*$struct["endurance"]+$struct["control"];
			$dexterity=$struct["vitality"]+3*$struct["control"]+3*$struct["intelligence"]+2*$struct["magic"];
			$agility=2*$struct["vitality"]+$struct["control"]+4*$struct["endurance"]+2*$struct["constitution"];
			$perception=4*$struct["soul"]["magic"]+3*$struct["intelligence"]+$struct["vitality"]+$struct["control"];
			
			return array("strength"=>$strength,"dexterity"=>$dexterity,"agility"=>$agility,"perception"=>$perception);
		}
		
		private static function makeAction($struct)
		{
			$hp=$struct["soul"]["vitality"]*$struct["body"]["constitution"]*$struct["body"]["endurance"];
			$mp=$struct["soul"]["magic"]*$struct["mind"]["control"]*$struct["mind"]["intelligence"];
			
			return array("hp"=>$hp,"mp"=>$mp);
		}
		
		private function metabolismFactor(&$needs)
		{
			return 100-((int)((2*$needs["hunger"]+$needs["tiredness"])/3));
		}
		
		private function moraleFactor(&$needs)
		{
			return 100-((int)((2*$needs["tiredness"]+$needs["hunger"])/3));
		}
		private function setWellBeing()
		{
			$this->base["well_being"]["metabolism"]=$this->current["structure"]["body"]["constitution"]*$this->current["structure"]["body"]["endurance"];
			$this->base["well_being"]["morale"]=$this->current["structure"]["mind"]["control"]*$this->current["structure"]["mind"]["intelligence"];
			$this->fixWellBeing();
		}
		private function fixWellBeing()
		{
			$this->current["well_being"]["metabolism"]=(int)($this->base["well_being"]["metabolism"]*static::metabolismFactor($this->current["needs"])/100);
			$this->current["well_being"]["morale"]=(int)($this->base["well_being"]["morale"]*static::moraleFactor($this->current["needs"])/100);
		}
		private function fixAction()
		{
			$this->current["action"]["hp"]=(int)($this->current["action"]["hp"]*($this->current["well_being"]["metabolism"]/$this->base["well_being"]["metabolism"]));
			$this->current["action"]["mp"]=(int)($this->current["action"]["mp"]*($this->current["well_being"]["morale"]/$this->base["well_being"]["morale"]));
		}
		
		public function change($which,$stat,$value)
		{
			$group=&$this->$which;
			$group["structure"][$stat]+=$value;
			$this->reStat($which);
			$this->fixWellBeing();
			$this->fixAction();
		}
	}
	
	class character
	{
		function __construct($id)
		{
			$this->pull($id);
		}
		
		public function pull($id)
		{
			global $db;
			$raw=array("base"=>array(),"current"=>array());
			$q=<<<SQL
			SELECT
				`character`.`id`, `character`.`soul`, `character`.`body`, `character`.`mind`,
				`soul`.`base_vitality`, `soul`.`base_magic`, `body`.`base_constitution`, `body`.`base_endurance`, `mind`.`base_control`, `mind`.`base_intelligence`,
				`soul`.`current_vitality`, `soul`.`current_magic`, `body`.`current_constitution`, `body`.`current_endurance`, `mind`.`current_control`, `mind`.`current_intelligence`,
				`character`.`hunger`, `character`.`tiredness`
			FROM `character` INNER JOIN `soul` ON `soul`.`id`=`character`.`soul` INNER JOIN `body` ON `body`.`id`=`character`.`body` INNER JOIN `mind` ON `mind`.`id`=`character`.`mind`
			WHERE `character`.`id`={$id}
SQL;
			$res=$db->query($q);
			list($this->id,$this->soul,$this->body,$this->mind,
			$raw["base"]["vitality"],$raw["base"]["magic"],$raw["base"]["constitution"],$raw["base"]["endurance"],$raw["base"]["control"],$raw["base"]["intelligence"],
			$raw["current"]["vitality"],$raw["current"]["magic"],$raw["current"]["constitution"],$raw["current"]["endurance"],$raw["current"]["control"],$raw["current"]["intelligence"],
			$raw["current"]["hunger"],$raw["current"]["tiredness"])=$res->fetch_array();
			
			$raw["base"]["hunger"]=100;
			$raw["base"]["tiredness"]=100;
			
			$this->stats=new stats($raw);
		}
		
		public function push()
		{
			global $db;
			$db->query("UPDATE `character` SET `hunger`={$this->stats->current["needs"]["hunger"]}, `tiredness`={$this->stats->current["needs"]["hunger"]} WHERE `id`={$this->id}");
			
			$db->query("UPDATE `soul` SET `base_vitality`={$this->stats->base["structure"]["vitality"]}, `base_magic`={$this->stats->base["structure"]["magic"]}, `current_vitality`={$this->stats->current["structure"]["vitality"]}, `current_magic`={$this->stats->current["structure"]["magic"]} WHERE `id`={$this->soul}");
			
			$db->query("UPDATE `body` SET `base_constitution`={$this->stats->base["structure"]["constitution"]}, `base_endurance`={$this->stats->base["structure"]["endurance"]}, `current_constitution`={$this->stats->current["structure"]["constitution"]}, `current_endurance`={$this->stats->current["structure"]["endurance"]} WHERE `id`={$this->body}");
			
			$db->query("UPDATE `mind` SET `base_control`={$this->stats->base["structure"]["control"]}, `base_intelligence`={$this->stats->base["structure"]["intelligence"]}, `current_control`={$this->stats->current["structure"]["control"]}, `current_intelligence`={$this->stats->current["structure"]["intelligence"]} WHERE `id`={$this->mind}");
		}
	}
	
	$db=new mysqli("localhost","hubris","5t6y7u","hubris");
	$reponse=new response();
	
// 	$character=new character(1);
// 	$character->stats->change("current","vitality",-10);
// 	print_r($character);
// 	$character->push();
// 	$character->pull(1);
// 	print_r($character);

	$spell=new spell(8);
	print_r($spell);
	$spell->turn();
	print_r($spell);
	$spell->push();
	print_r($spell);
?>